#pragma once
#include "matrix4.h"

BEGIN_NAMESPACE_OXGE

template <typename T>
class MATHLIB_TRANSFER QuaternionT 
{
public:
	typedef QuaternionT<T> QuatType;
	typedef Matrix4T<T> MatrixType;
	
	QuaternionT(){}

	QuaternionT(T w, T x, T y, T z){	Set(w,x,y,z);	}

	QuatType& Set(T qw, T qx, T qy, T qz){	w = qw; x=qx; y=qy; z=qz;	return *this; }

	QuatType operator*(const QuatType& quatR)const;

	QuatType operator+(const QuatType& quatR)const
	{
		return QuatType(w+quatR.w, x+quatR.x, y+quatR.y, z+quatR.z);
	}

	QuatType operator*(const T& rVal)const{
		return QuatType(w*rVal, x*rVal, y*rVal,z*rVal);
	}

	QuatType ConjQuat()const{
		return QuatType(w,-x,-y,-z);
	}

	T Norm()const
	{
		return sqrt(w*w + x*x + y*y + z*z);
	}

	QuatType GetInverse()const{
		return (*this)*(1.0/Norm());
	}

	MatrixType GetMatrix()const{
		return UNIT_W_MAT*w + UNIT_X_MAT*x + UNIT_Y_MAT*y + UNIT_Z_MAT*z;
	}

	//
	static void UnitTest();

public:
	union{
		struct{ T w,x,y,z; };
		struct{ T a1,a2,a3,a4; };
	};

	
	const static MatrixType UNIT_X_MAT;
	const static MatrixType UNIT_Y_MAT;
	const static MatrixType UNIT_Z_MAT;
	const static MatrixType UNIT_W_MAT;

	static const QuatType ZERO;
	static const QuatType UNIT_X;
	static const QuatType UNIT_Y;
	static const QuatType UNIT_Z;
	static const QuatType UNIT_W;
	static const QuatType UNIT_SCALE;


	
};




//Global io
template<typename T>
std::ostream& operator<<(std::ostream& os, QuaternionT<T> quat)
{	
	os << quat.w << "+" << quat.x << "i+" << quat.y << "j+" << quat.z <<"k";		
	return os;
}

template<typename T>
std::istream& operator>>(std::istream& is, QuaternionT<T>& quat);

END_NAMESPACE_OXGE